#include "L_System.h"

//cwc::glShaderManager SM;
//cwc::glShader *shader;

const int WINDOW_WIDTH = 1000;
const int WINDOW_HEIGHT = 800;
float aspectRatio;

L_System *yggdrasil, *firmament, *law, *life, *bush;
Stochastic_L_System *verdancy;
edgeProduction *stochasticEdge;
Parametric_L_System *fulfillment, *sufficiency, *peace, *alhalvalost, *leaved;

Turtle* turtle;
Parametric_Turtle* pTurtle;
std::vector<Module> legacy;
std::string genome;
int eons, cycles;

bool once = false;
float *val = new float();

float animate, trueEdge, growth;
bool stopGrowth;

void DemoLight()
   {
     glEnable(GL_LIGHTING);
     glEnable(GL_LIGHT0);
	 glEnable(GL_LIGHT1);
	 glEnable(GL_LIGHT2);
     glEnable(GL_NORMALIZE);
     
     // Light model parameters:
     // -------------------------------------------
     
     GLfloat lmKa[] = {0.0, 0.0, 0.0, 0.0 };
     glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmKa);
     
     glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, 1.0);
     glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0.0);
     
     // -------------------------------------------
     // Spotlight Attenuation
     
     GLfloat spot_direction[] = {-1.0, 0.0, -1.0 };
     GLint spot_exponent = 30;
     GLint spot_cutoff = 180;
     
     glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);
     glLighti(GL_LIGHT0, GL_SPOT_EXPONENT, spot_exponent);
     glLighti(GL_LIGHT0, GL_SPOT_CUTOFF, spot_cutoff);

	 GLfloat spot_direction2[] = {1.0, 0.0, -1.0};
	 glLightfv(GL_LIGHT1, GL_SPOT_DIRECTION, spot_direction2);
	 glLighti(GL_LIGHT1, GL_SPOT_EXPONENT, spot_exponent);
     glLighti(GL_LIGHT1, GL_SPOT_CUTOFF, spot_cutoff);

	 GLfloat spot_direction3[] = {1.0, 0.0, -1.0};
	 glLightfv(GL_LIGHT2, GL_SPOT_DIRECTION, spot_direction3);
	 glLighti(GL_LIGHT2, GL_SPOT_EXPONENT, spot_exponent);
     glLighti(GL_LIGHT2, GL_SPOT_CUTOFF, spot_cutoff);
    
     GLfloat Kc = 1.0;
     GLfloat Kl = 0.0;
     GLfloat Kq = 0.0;
     
     glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION,Kc);
     glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, Kl);
     glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, Kq);

	 glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION,Kc);
     glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, Kl);
     glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, Kq);
     
     
     // ------------------------------------------- 
     // Lighting parameters:

     GLfloat light_pos[] = {10.0f, 10.0f, -25.0f, 1.0f};
     GLfloat light_Ka[]  = {1.0f, 0.5f, 0.5f, 1.0f};
     GLfloat light_Kd[]  = {1.0f, 1.0f, 1.0f, 1.0f};
     GLfloat light_Ks[]  = {1.0f, 1.0f, 1.0f, 1.0f};

     glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
     glLightfv(GL_LIGHT0, GL_AMBIENT, light_Ka);
     glLightfv(GL_LIGHT0, GL_DIFFUSE, light_Kd);
     glLightfv(GL_LIGHT0, GL_SPECULAR, light_Ks);

	 GLfloat light_pos2[] = {-10.0f, 10.0f, -25.0f, 1.0f};
     GLfloat light_Ka2[]  = {1.0f, 0.5f, 0.5f, 1.0f};
     GLfloat light_Kd2[]  = {1.0f, 1.0f, 1.0f, 1.0f};
     GLfloat light_Ks2[]  = {1.0f, 1.0f, 1.0f, 1.0f};

     glLightfv(GL_LIGHT1, GL_POSITION, light_pos2);
     glLightfv(GL_LIGHT1, GL_AMBIENT, light_Ka2);
     glLightfv(GL_LIGHT1, GL_DIFFUSE, light_Kd2);
     glLightfv(GL_LIGHT1, GL_SPECULAR, light_Ks2);

     // -------------------------------------------
     // Material parameters:

	 /*
     GLfloat material_Ka[] = {0.135f, 0.2225f, 0.1575f, 0.95f};
     GLfloat material_Kd[] = {0, 0, 0, 0};
     GLfloat material_Ks[] = {0.316228f, 0.316228f, 0.316228f, 0.95f};
     GLfloat material_Ke[] = {0.1f, 0.0f, 0.0f, 0.0f};
     GLfloat material_Se = 0.8f;
	 */
	 
	 
	 GLfloat material_Ka[] = {0, 0.1f, 0, 1.0f};
     GLfloat material_Kd[] = {0, 1.0f, 0, 1.0f};
     GLfloat material_Ks[] = {0.2f, 0.8f, 0.2f, 1.0f};
     GLfloat material_Ke[] = {0.1f, 0.0f, 0.0f, 0.0f};
     GLfloat material_Se = 2.0f;
	 
	 
     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_Ka);
     glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_Kd);
     glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_Ks);
     glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, material_Ke);
     glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material_Se);
   }

void initializeStochastic()
{
	stochasticEdge = new edgeProduction();
	stochasticEdge->productions.push_back("F[+F]F[-F]F");
	stochasticEdge->productions.push_back("F[+F]F");
	stochasticEdge->productions.push_back("F[-F]F");
	stochasticEdge->weights = new float[3];
	stochasticEdge->weights[0] = 0.33;
	stochasticEdge->weights[1] = 0.33;
	stochasticEdge->weights[2] = 0.33;
	verdancy = new Stochastic_L_System("F", stochasticEdge, 0.02, 20);
}

void initializeParametric()
{
	std::vector<Production> productions;
	std::vector<Module> parametricProductions, axiom;
	std::vector<float> params;
	std::vector<Expression> productionExpressions;
	Production *newProduction;
	Module *newModule;
	Expression *newExpression;

	newExpression = new Expression(7, 0, 0, 0.3f, true);				
	productionExpressions.push_back(*newExpression);
	newModule = new Module('F', productionExpressions);					//F(x * p), p = 0.3
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('+', productionExpressions);					//+(a)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	float h = (0.3 * 0.7);
	h = sqrt(h);
	newExpression = new Expression(7, 0, 0, h, true);				
	productionExpressions.push_back(*newExpression);
	newModule = new Module('F', productionExpressions);					//F(x * h), h = (p * q) ^ 0.5, q = c - p = 0.7
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('-', productionExpressions);					//-(a)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('-', productionExpressions);					//-(a)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(7, 0, 0, h, true);				
	productionExpressions.push_back(*newExpression);
	newModule = new Module('F', productionExpressions);					//F(x * h), h = (p * q) ^ 0.5, q = c - p = 0.7
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('+', productionExpressions);					//+(a)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(7, 0, 0, 0.7f, true);				
	productionExpressions.push_back(*newExpression);
	newModule = new Module('F', productionExpressions);					//F(x * q), p = 0.7
	parametricProductions.push_back(*newModule);
	
	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means there is no condition
	newProduction = new Production('F', 1, *newExpression, parametricProductions);
	productions.push_back(*newProduction);

	params.push_back(1);
	newModule = new Module('F', params); 
	axiom.push_back(*newModule);

	fulfillment = new Parametric_L_System(0, 85, axiom, productions);
}

void initializeOtherParametric()
{
	std::vector<Production> productions;
	std::vector<Module> parametricProductions, axiom;
	std::vector<float> params;
	std::vector<Expression> productionExpressions;
	Production *newProduction;
	Module *newModule;
	Expression *newExpression;

	/************************************************FIRST PRODUCTION***********************************************************************/			
	params.push_back(0.05f);
	newModule = new Module('F', params);								//F(0.1)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('[', productionExpressions);					//[
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('+', productionExpressions);					//+
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();		
	productionExpressions.push_back(*newExpression);
	newModule = new Module('A', productionExpressions);					//A
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module(']', productionExpressions);					//]
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('[', productionExpressions);					//[
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('-', productionExpressions);					//-
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();		
	productionExpressions.push_back(*newExpression);
	newModule = new Module('A', productionExpressions);					//A
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module(']', productionExpressions);					//]
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means there is no condition
	newProduction = new Production('A', 0, *newExpression, parametricProductions);	//A -> F(1)[+A][-A]
	productions.push_back(*newProduction);			
	parametricProductions.clear();
	/**************************************************************************************************************************************/

	/*****************************************************SECOND PRODUCTION****************************************************************/
	float r = sqrt(2);
	newExpression = new Expression(7, 0, 0, r, true);				
	productionExpressions.push_back(*newExpression);
	newModule = new Module('F', productionExpressions);								//F(s * r)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();												//Blank expression means there is no condition
	newProduction = new Production('F', 1, *newExpression, parametricProductions);	//F(s) -> F(s * r)
	productions.push_back(*newProduction);
	parametricProductions.clear();
	/**************************************************************************************************************************************/

	newModule = new Module('A'); 
	axiom.push_back(*newModule);

	sufficiency = new Parametric_L_System(0, 82, axiom, productions);
}

void monopodialParametric()
{
	float trunkContractionRatio = 0.9f;
	float branchContractionRatio = 0.7f;
	float firstDaughterBranchAngle = 30;
	float secondDaughterBranchAngle = -30;
	float divergenceAngle = 137.5f;
	float widthReductionRate = 0.707f;

	std::vector<Production> productions;
	std::vector<Module> parametricProductions, axiom;
	std::vector<float> params;
	std::vector<Expression> productionExpressions;
	Production *newProduction;
	Module *newModule;
	Expression *newExpression;

	/*********************FIRST PRODUCTION***************************/
	newExpression = new Expression(5, 1, 0, 0, true);					//We need to preserve the parameter, so Add 0
	productionExpressions.push_back(*newExpression);
	newModule = new Module('!', productionExpressions);					//!(w + 0)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(5, 0, 0, 0, true);					//Pay attention to the operand -- this system has two
	productionExpressions.push_back(*newExpression);					//parameters l & w and their position in the input parameter 
	newModule = new Module('F', productionExpressions);					//list must be specified correctly - F(l)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('[', productionExpressions);					//[
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(firstDaughterBranchAngle);							//For passing constants just add parameters rather than expressions
	newModule = new Module('&', params);								//&(branchAngle)
	parametricProductions.push_back(*newModule);

	params.clear();

	newExpression = new Expression(7, 0, 0, branchContractionRatio, true);				
	productionExpressions.push_back(*newExpression);
	newExpression = new Expression(7, 1, 0, widthReductionRate, true);
	productionExpressions.push_back(*newExpression);
	newModule = new Module('B', productionExpressions);					//B(l*r2, w*wr)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module(']', productionExpressions);					//]
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(divergenceAngle);									//For passing constants just add parameters rather than expressions
	newModule = new Module('-', params);							//-(divergenceAngle)	This is noted in the notes as '/(d)', but remember
	parametricProductions.push_back(*newModule);						//we swapped notation to suit the same turtle for 2D and 3D analysis
																		//So:	'/' = '-'	'\(escapecharacter)\' = '+'
	params.clear();

	newExpression = new Expression(7, 0, 0, trunkContractionRatio, true);				
	productionExpressions.push_back(*newExpression);
	newExpression = new Expression(7, 1, 0, widthReductionRate, true);
	productionExpressions.push_back(*newExpression);
	newModule = new Module('A', productionExpressions);					//A(l*r1, w*wr)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means there is no condition
	newProduction = new Production('A', 2, *newExpression, parametricProductions);	//A(l, w) -> !(w)F(l)[&(a0)B(l*r2,w*wr)]/(d)A(l*r1,w*wr)
	productions.push_back(*newProduction);			
	parametricProductions.clear();
	/**************************************************************************************************************************************/

	/*******************************************SECOND PRODUCTION************************************************************************/
	newExpression = new Expression(5, 1, 0, 0, true);					//We need to preserve the parameter, so Add 0
	productionExpressions.push_back(*newExpression);
	newModule = new Module('!', productionExpressions);					//!(w + 0)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(5, 0, 0, 0, true);					//Pay attention to the operand -- this system has two
	productionExpressions.push_back(*newExpression);					//parameters l & w and their position in the input parameter 
	newModule = new Module('F', productionExpressions);					//list must be specified correctly - F(l)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('[', productionExpressions);					//[
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(secondDaughterBranchAngle);							//For passing constants just add parameters rather than expressions
	newModule = new Module('/', params);					///(branchAngle)
	parametricProductions.push_back(*newModule);						//we swapped notation to suit the same turtle for 2D and 3D analysis
																		//So:	'/' = '-'	'\(escapecharacter)\' = '+'
	params.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('$', productionExpressions);					//$
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(7, 0, 0, branchContractionRatio, true);				
	productionExpressions.push_back(*newExpression);
	newExpression = new Expression(7, 1, 0, widthReductionRate, true);
	productionExpressions.push_back(*newExpression);
	newModule = new Module('C', productionExpressions);					//C(l*r2, w*wr)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module(']', productionExpressions);					//]
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(divergenceAngle);									//For passing constants just add parameters rather than expressions
	newModule = new Module('-', params);							//-(divergenceAngle)	This is noted in the notes as '/(d)', but remember
	parametricProductions.push_back(*newModule);						//we swapped notation to suit the same turtle for 2D and 3D analysis
																		//So:	'/' = '-'	'\(escapecharacter)\' = '+'
	params.clear();

	newExpression = new Expression(7, 0, 0, trunkContractionRatio, true);				
	productionExpressions.push_back(*newExpression);
	newExpression = new Expression(7, 1, 0, widthReductionRate, true);
	productionExpressions.push_back(*newExpression);
	newModule = new Module('C', productionExpressions);					//C(l*r1, w*wr)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means there is no condition
	newProduction = new Production('B', 2, *newExpression, parametricProductions);	//B(l, w) -> !(w)F(l)[/(a0)$C(l*r2,w*wr)]C(l*r1,w*wr)
	productions.push_back(*newProduction);			
	parametricProductions.clear();
	productionExpressions.clear();
	/************************************************************************************************************************************/

	/*******************************************************THIRD PRODUCTION*********************************************************/
	newExpression = new Expression(5, 1, 0, 0, true);					//We need to preserve the parameter, so Add 0
	productionExpressions.push_back(*newExpression);
	newModule = new Module('!', productionExpressions);					//!(w + 0)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(5, 0, 0, 0, true);					//Pay attention to the operand -- this system has two
	productionExpressions.push_back(*newExpression);					//parameters l & w and their position in the input parameter 
	newModule = new Module('F', productionExpressions);					//list must be specified correctly - F(l + 0)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('[', productionExpressions);					//[
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(secondDaughterBranchAngle);							//For passing constants just add parameters rather than expressions
	newModule = new Module('\\', params);				// \\(branchAngle)
	parametricProductions.push_back(*newModule);						//we swapped notation to suit the same turtle for 2D and 3D analysis
																		//So:	'/' = '-'	'\(escapecharacter)\' = '+'
	params.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('$', productionExpressions);					//$
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(7, 0, 0, branchContractionRatio, true);				
	productionExpressions.push_back(*newExpression);
	newExpression = new Expression(7, 1, 0, widthReductionRate, true);
	productionExpressions.push_back(*newExpression);
	newModule = new Module('B', productionExpressions);					//B(l*r2, w*wr)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module(']', productionExpressions);					//]
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(7, 0, 0, trunkContractionRatio, true);				
	productionExpressions.push_back(*newExpression);
	newExpression = new Expression(7, 1, 0, widthReductionRate, true);
	productionExpressions.push_back(*newExpression);
	newModule = new Module('B', productionExpressions);					//B(l*r1, w*wr)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means there is no condition
	newProduction = new Production('C', 2, *newExpression, parametricProductions);	//C(l, w) -> !(w)F(l)[\\(a0)$B(l*r2,w*wr)]B(l*r1,w*wr)
	productions.push_back(*newProduction);			
	parametricProductions.clear();
	/***************************************************************************************************************************************/

	params.push_back(1);
	params.push_back(10);
	newModule = new Module('A', params); 
	axiom.push_back(*newModule);

	peace = new Parametric_L_System(0, 0, axiom, productions);
}

void sympodialParametric()
{
	float firstBranchContractionRatio = 0.9f;
	float secondBranchContractionRatio = 0.8f;
	float firstDaughterBranchAngle = 5;
	float secondDaughterBranchAngle = 65;
	const float divergenceAngle = 120;
	float widthReductionRate = 0.707f;

	std::vector<Production> productions;
	std::vector<Module> parametricProductions, axiom;
	std::vector<float> params;
	std::vector<Expression> productionExpressions;
	Production *newProduction;
	Module *newModule;
	Expression *newExpression;

	/*********************FIRST PRODUCTION***************************/
	newExpression = new Expression(5, 1, 0, 0, true);					//We need to preserve the parameter, so Add 0
	productionExpressions.push_back(*newExpression);
	newModule = new Module('!', productionExpressions);					//!(w + 0)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(5, 0, 0, 0, true);					//Pay attention to the operand -- this system has two
	productionExpressions.push_back(*newExpression);					//parameters l & w and their position in the input parameter 
	newModule = new Module('F', productionExpressions);					//list must be specified correctly - F(l)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('[', productionExpressions);					//[
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(firstDaughterBranchAngle);							//For passing constants just add parameters rather than expressions
	newModule = new Module('&', params);								//&(branchAngle)
	parametricProductions.push_back(*newModule);

	params.clear();

	newExpression = new Expression(7, 0, 0, firstBranchContractionRatio, true);				
	productionExpressions.push_back(*newExpression);
	newExpression = new Expression(7, 1, 0, widthReductionRate, true);
	productionExpressions.push_back(*newExpression);
	newModule = new Module('B', productionExpressions);					//B(l*r1, w*wr)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module(']', productionExpressions);					//]
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(divergenceAngle);									//For passing constants just add parameters rather than expressions
	newModule = new Module('-', params);								//-(divergenceAngle)	This is noted in the notes as '/(d)', but remember
	parametricProductions.push_back(*newModule);						//we swapped notation to suit the same turtle for 2D and 3D analysis
																		//So:	'/' = '-'	'\(escapecharacter)\' = '+'
	params.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('[', productionExpressions);					//[
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(secondDaughterBranchAngle);							//For passing constants just add parameters rather than expressions
	newModule = new Module('&', params);								//&(branchAngle)
	parametricProductions.push_back(*newModule);

	params.clear();

	newExpression = new Expression(7, 0, 0, secondBranchContractionRatio, true);				
	productionExpressions.push_back(*newExpression);
	newExpression = new Expression(7, 1, 0, widthReductionRate, true);
	productionExpressions.push_back(*newExpression);
	newModule = new Module('B', productionExpressions);					//B(l*r2, w*wr)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module(']', productionExpressions);					//]
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means there is no condition
	newProduction = new Production('A', 2, *newExpression, parametricProductions);	//A(l, w) -> !(w)F(l)[&(a0)B(l*r2,w*wr)]/(d)B(l*r2,w*wr)
	productions.push_back(*newProduction);			
	parametricProductions.clear();
	/**************************************************************************************************************************************/

	/*******************************************SECOND PRODUCTION************************************************************************/
	newExpression = new Expression(5, 1, 0, 0, true);					//We need to preserve the parameter, so Add 0
	productionExpressions.push_back(*newExpression);
	newModule = new Module('!', productionExpressions);					//!(w + 0)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(5, 0, 0, 0, true);					//Pay attention to the operand -- this system has two
	productionExpressions.push_back(*newExpression);					//parameters l & w and their position in the input parameter 
	newModule = new Module('F', productionExpressions);					//list must be specified correctly - F(l)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('[', productionExpressions);					//[
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(firstDaughterBranchAngle);							//For passing constants just add parameters rather than expressions
	newModule = new Module('\\', params);								// \\(branchAngle)
	parametricProductions.push_back(*newModule);

	params.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('$', productionExpressions);					//$
	parametricProductions.push_back(*newModule);

	newExpression = new Expression(7, 0, 0, firstBranchContractionRatio, true);				
	productionExpressions.push_back(*newExpression);
	newExpression = new Expression(7, 1, 0, widthReductionRate, true);
	productionExpressions.push_back(*newExpression);
	newModule = new Module('B', productionExpressions);					//B(l*r1, w*wr)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module(']', productionExpressions);					//]
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('[', productionExpressions);					//[
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(secondDaughterBranchAngle);							//For passing constants just add parameters rather than expressions
	newModule = new Module('/', params);								///(branchAngle)
	parametricProductions.push_back(*newModule);

	params.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('$', productionExpressions);					//$
	parametricProductions.push_back(*newModule);

	newExpression = new Expression(7, 0, 0, secondBranchContractionRatio, true);				
	productionExpressions.push_back(*newExpression);
	newExpression = new Expression(7, 1, 0, widthReductionRate, true);
	productionExpressions.push_back(*newExpression);
	newModule = new Module('B', productionExpressions);					//B(l*r1, w*wr)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module(']', productionExpressions);					//]
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means there is no condition
	newProduction = new Production('B', 2, *newExpression, parametricProductions);	//A(l, w) -> !(w)F(l)[&(a0)B(l*r2,w*wr)]/(d)A(l*r1,w*wr)
	productions.push_back(*newProduction);			
	parametricProductions.clear();
	/************************************************************************************************************************************/

	params.push_back(1);
	params.push_back(10);
	newModule = new Module('A', params); 
	axiom.push_back(*newModule);

	alhalvalost = new Parametric_L_System(0, 0, axiom, productions);
}

void parametricLeaf()
{
	float trunkContractionRatio = 0.9f;
	float branchContractionRatio = 0.7f;
	float firstLeafAngle = 30;
	float firstLeafTilt = 40;
	float leafWhorl = 90;
	float divergenceAngle = 180;
	float widthReductionRate = 0.707f;

	std::vector<Production> productions;
	std::vector<Module> parametricProductions, axiom;
	std::vector<float> params;
	std::vector<Expression> productionExpressions;
	Production *newProduction;
	Module *newModule;
	Expression *newExpression;

	/*********************FIRST PRODUCTION***************************/
	newExpression = new Expression(5, 1, 0, 0, true);					//We need to preserve the parameter, so Add 0
	productionExpressions.push_back(*newExpression);
	newModule = new Module('!', productionExpressions);					//!(w + 0)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(5, 0, 0, 0, true);					//Pay attention to the operand -- this system has two
	productionExpressions.push_back(*newExpression);					//parameters l & w and their position in the input parameter 
	newModule = new Module('F', productionExpressions);					//list must be specified correctly - F(l)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(leafWhorl);									//For passing constants just add parameters rather than expressions
	newModule = new Module('+', params);								//&(leafTilt)
	parametricProductions.push_back(*newModule);

	params.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('[', productionExpressions);					//[
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(firstLeafAngle);							//For passing constants just add parameters rather than expressions
	newModule = new Module('&', params);								//&(branchAngle)
	parametricProductions.push_back(*newModule);

	params.clear();

	params.push_back(firstLeafTilt);							//For passing constants just add parameters rather than expressions
	newModule = new Module('+', params);								//&(leafTilt)
	parametricProductions.push_back(*newModule);

	params.clear();

	newExpression = new Expression(5, 0, 0, 0, true);					//Pay attention to the operand -- this system has two
	productionExpressions.push_back(*newExpression);					//parameters l & w and their position in the input parameter 
	newModule = new Module('L', productionExpressions);					//list must be specified correctly - L(l)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module(']', productionExpressions);					//]
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(divergenceAngle);									//For passing constants just add parameters rather than expressions
	newModule = new Module('-', params);							//-(divergenceAngle)	This is noted in the notes as '/(d)', but remember
	parametricProductions.push_back(*newModule);						//we swapped notation to suit the same turtle for 2D and 3D analysis
																		//So:	'/' = '-'	'\(escapecharacter)\' = '+'
	params.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module('[', productionExpressions);					//[
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	params.push_back(firstLeafAngle);							//For passing constants just add parameters rather than expressions
	newModule = new Module('&', params);								//&(branchAngle)
	parametricProductions.push_back(*newModule);

	params.clear();

	params.push_back(firstLeafTilt);							//For passing constants just add parameters rather than expressions
	newModule = new Module('+', params);								//+(leafTilt)
	parametricProductions.push_back(*newModule);

	params.clear();

	newExpression = new Expression(5, 0, 0, 0, true);					//Pay attention to the operand -- this system has two
	productionExpressions.push_back(*newExpression);					//parameters l & w and their position in the input parameter 
	newModule = new Module('L', productionExpressions);					//list must be specified correctly - L(l)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means just pass the parameter without modifying
	productionExpressions.push_back(*newExpression);
	newModule = new Module(']', productionExpressions);					//]
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression(7, 0, 0, trunkContractionRatio, true);				
	productionExpressions.push_back(*newExpression);
	newExpression = new Expression(7, 1, 0, widthReductionRate, true);
	productionExpressions.push_back(*newExpression);
	newModule = new Module('A', productionExpressions);					//A(l*r1)
	parametricProductions.push_back(*newModule);

	productionExpressions.clear();

	newExpression = new Expression();									//Blank expression means there is no condition
	newProduction = new Production('A', 2, *newExpression, parametricProductions);	//A(l) -> !(w*wr)F(l)[&(a0)L(l)]/(d)[&(a1)L(l)]A(l*r0)
	productions.push_back(*newProduction);			
	parametricProductions.clear();
	/**************************************************************************************************************************************/

	params.push_back(1);
	params.push_back(5);
	newModule = new Module('A', params); 
	axiom.push_back(*newModule);

	leaved = new Parametric_L_System(0, 0, axiom, productions);
}

void init()
{
	aspectRatio = WINDOW_WIDTH/WINDOW_HEIGHT;
	glShadeModel(GL_SMOOTH);
	glEnable(GL_DEPTH_TEST);

	//DemoLight();
	
	/*shader = SM.loadfromFile("vertexshader.txt","fragmentshader.txt"); // load (and compile, link) from file
		  if (shader==0) 
			  std::cout << "Error Loading, compiling or linking shader\n";
	
	shader = SM.loadfromFile("vertexshader.txt","toonshader.txt"); // load (and compile, link) from file
		  if (shader==0) 
			  std::cout << "Error Loading, compiling or linking shader\n";
	*/
	
	//Animation parameter iterated every frame
	animate = 0.01f;
	stopGrowth = false;

	yggdrasil = new L_System("F", "F[+F]F[-F]F", 1, 25.7);
	firmament = new L_System("F", "FF-[-F+F+F]+[+F-F-F]", 0.025, 22.5);

	std::vector<std::string> lawProductions;
	lawProductions.push_back("[+F]F-F[F]");
	law = new L_System("F", lawProductions, 0.15f, 10);

	std::vector<std::string> lifeProductions;
	lifeProductions.push_back("FF");
	lifeProductions.push_back("F-[[1]+1]+F[+F1]-1");
	life = new L_System("1", lifeProductions, 0.01f, 22.5);

	std::vector<std::string> bushProductions;
	bushProductions.push_back("2-----F");
	bushProductions.push_back("[&F3!1]-----'[&F3!1]-------'[&F3!1]");
	bushProductions.push_back("F3");
	bushProductions.push_back("['''^^{L}]");
	bush = new L_System("1", bushProductions, 0.5f, 22.5);
	trueEdge = bush->edgeLength;

	//initializeStochastic();

	//initializeParametric();

	//initializeOtherParametric();

	//monopodialParametric();

	sympodialParametric();

	//parametricLeaf();

	
	turtle = new Turtle(0.0f, 0, 0, law);
	eons = 6;
	law->propogate(eons);
	genome = law->returnList;
	trueEdge = law->edgeLength;
	

	//leaved->propogate(12);
	//legacy = leaved->returnList;
	//turtle = new Turtle(0.0f, -5.0f, -25.0f, -90, 0, 0, bush);
	cycles = 6;

	pTurtle = new Parametric_Turtle(0, -4.0f, -25, -90, 0, 90, leaved);

	/*fulfillment -> propogate(2);
	legacy = fulfillment->returnList;

	sufficiency -> propogate(8);
	legacy = sufficiency->returnList;

	peace -> propogate(10);
	legacy = peace->returnList;*/

	//alhalvalost -> propogate(10);
	//legacy = alhalvalost->returnList;

//	pTurtle->lsys = sufficiency;

	//pTurtle->lsys = peace;

	pTurtle -> lsys = alhalvalost;


	//yggdrasil->propogate(eons);
	//genome = yggdrasil->returnList;

	//turtle->lsys=firmament;
	//firmament->propogate(eons-2);
	//genome = firmament->returnList;
	//trueEdge = firmament->edgeLength;

	//turtle -> lsys = law;
	//eons = 7;
	//law->propogate(eons);
	//genome = law->returnList;

	/*turtle -> lsys = life;
	life->propogate(eons);
	genome = life->returnList;
	trueEdge = life->edgeLength;
*/
	
	/*turtle -> lsys = verdancy;
	turtle->in3D = false;
	turtle->X = 0;
	turtle->Y = -1;
	turtle->Z = 0;
	turtle->phi[0] = 0;
	turtle->phi[1] = 0;
	turtle->phi[2] = 0;
	verdancy -> propogate(eons);
	genome = verdancy -> returnList;*/
	
	/*
	turtle->lsys = bush;
	bush->propogate(cycles);
	genome = bush->returnList;
	trueEdge = bush->edgeLength;
	*/

	glEnable(GL_LINE_SMOOTH);
}

void drawCircle()
{
	int i;
	glBegin(GL_POLYGON);
		for(i = 0; i < 360; i++)
		{
			glVertex2f(cos(i*DEG2RAD), sin(i*DEG2RAD));
		}
	glEnd();
}

void drawLine()
{
	glBegin(GL_LINES);
		glVertex2f(0,0);
		glVertex2f(0,1);
	glEnd();
}

void drawLine3D()
{
	glBegin(GL_LINES);
		glVertex3f(0,0,0);
		glVertex3f(0,0,1);
	glEnd();

}

void drawLeaf3D()
{
	glBegin(GL_POLYGON);
		glVertex3f(0, 0, 0);
		glVertex3f(0.1, 0, 0.15f);
		glVertex3f(0.1, 0, 0.35f);
		glVertex3f(0, 0, 0.5f);
		glVertex3f(-0.1, 0, 0.35f);
		glVertex3f(-0.1, 0, 0.15f);
	glEnd();
}

void display()
{
	
	//3D Mode
	/*glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45, 1, 1, 100);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.0f, -5.0f, 0.0f,
				0.0, 0.0f, -25.0f, 
					0.0f, 1.0f, 0.0f);*/
		
	
	//2D Mode
	gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);/* clear the screen*/
	glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
	//glClearColor(1, 1, 1, 1);
	//glLineWidth(5.0f);
	glColor3f(0, 1, 0);
	//drawLeaf3D();
	//drawLine();
	//turtle->stir(genome);
	turtle->stir(genome);
	//if(shader)shader->begin();
	//pTurtle->phi[2] = -100*animate;
	//turtle->phi[2] = -10*animate;
	if(!stopGrowth)
	{
		turtle->lsys->edgeLength = growth * trueEdge;
		turtle->lsys->theta = animate;
	}
	
	/*
	glLineWidth(5);
	glRotatef(90, 1, 0, 0);
	drawLine3D();
	glGetFloatv(GL_LINE_WIDTH, val);
	if(!once)
		std::cout << *val << "\n";
	
	glTranslatef(1, 0, 0);
	glLineWidth(0.01f);
	drawLine3D();
	glGetFloatv(GL_LINE_WIDTH, val);
	if(!once)
		std::cout << *val << "\n";

	glTranslatef(-2, 0, 0);
	glLineWidth(50);
	drawLine3D();
	glGetFloatv(GL_LINE_WIDTH, val);
	if(!once)
		std::cout << *val << "\n";

	once = true;
	*/
	//turtle->lsys->
	//turtle->stir(genome);
	//pTurtle->stir(legacy);
	//if(shader)shader->end();
	


	//glutSolidDodecahedron();
	//drawLeaf3D();

	glFlush();
}

void animation()
{
	if(!stopGrowth)
	{
		animate += 0.1f;
		growth = sin(animate*0.01f);
		//if(growth > 0.99f)
		//	stopGrowth = true;
	}
	glutPostRedisplay();
}

void refresh(unsigned char key, int x, int y)
{
	if(key == 'w')
	{
		verdancy -> returnList = "F";
		verdancy -> propogate(eons);
		genome = verdancy -> returnList;
	}
	if(key == 'd')
	{
		pTurtle->phi[2] +=10;
	}
	if(key == 32) //Spacebar
	{
		stopGrowth = !stopGrowth;
		std::cout << "Edge Length: " << turtle->lsys->edgeLength << "\n";
		std::cout << "Angle: " << turtle->lsys->theta << "\n";
	}
}

int main(int argc, char** argv)
{
	/* create a window, position it, and name it */
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
	glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
	glutInitWindowPosition(100, 100);
	glutCreateWindow("L-Systems");

	/* create a callback routine for when the mouse is clicked */
	//glutMouseFunc(myMouse);

	glutKeyboardFunc(refresh);

	/* create a callback routine for when the program is idle */
	glutIdleFunc(animation);

	/* create a callback routine for (re-)display */
	glutDisplayFunc(display);

	//BuildPopupMenu ();  //This is going to be a function you write to initialize the menu
    //glutAttachMenu (GLUT_RIGHT_BUTTON);  //This attaches the menu to the right mouse click  button

	/* init some states */
	init();
	
	/* entering the event loop */
	glutMainLoop();

	/* code here will not be executed */
}



